package com.zero.business.order.service.impl;

import cn.hutool.core.util.IdUtil;
import com.github.pagehelper.PageHelper;
import com.zero.business.order.feign.OrderBicycleFeign;
import com.zero.business.order.feign.OrderMemberFeign;
import com.zero.business.order.mapper.OrderMapper;
import com.zero.business.order.service.OrderService;
import com.zero.common.constants.ServiceConstants;
import com.zero.common.domain.Page;
import com.zero.common.exception.ServiceException;
import com.zero.domain.Bicycle;
import com.zero.domain.Member;
import com.zero.domain.Order;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;

@Service
public class OrderServiceImpl implements OrderService{

    @Resource
    private OrderMapper orderMapper;

    @Resource
    private OrderMemberFeign orderMemberFeign;

    @Resource
    private OrderBicycleFeign orderBicycleFeign;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public int deleteByPrimaryKey(Integer orderId) {
        return orderMapper.deleteByPrimaryKey(orderId);
    }

    @Override
    public int insert(Order record) {
        return orderMapper.insert(record);
    }

    @Override
    public int insertSelective(Order record) {
        return orderMapper.insertSelective(record);
    }

    @Override
    public Order selectByPrimaryKey(Integer orderId) {
        return orderMapper.selectByPrimaryKey(orderId);
    }

    @Override
    public int updateByPrimaryKeySelective(Order record) {
        return orderMapper.updateByPrimaryKeySelective(record);
    }

    @Override
    public int updateByPrimaryKey(Order record) {
        return orderMapper.updateByPrimaryKey(record);
    }


    /*
    *   会员使用单车
    *           步骤：1.根据 userId 查询会员信息，根据 bicycleId查询车辆信息， 并填充 订单数据
    *                 2.新增一条订单记录
    *                 3.更新单车状态 为被使用中的状态   statue=1
    *                 4.更新会员状态 为使用中的状态     statue=1
    * */
    @Override
    @Transactional  // 多次进行与数据库进行增删改类型的I/O操作，因此得加事务
    public void useBicycle(Integer userId, Integer bicycleId) {
        // 1.根据 userId 查询会员信息
        Member member = orderMemberFeign.selectByPrimaryKey(userId);
        // 根据 bicycleId 查询车辆信息
        Bicycle bicycle = orderBicycleFeign.selectByPrimaryKey(bicycleId);

        // 填充订单数据
        Order order = Order.builder()
                .userId(userId)    // 用户ID
                .bicycleId(bicycleId)   // 单车ID
                .bicycleSn(bicycle.getBicycleSn())  // 单车编号
                .orderSn(ServiceConstants.ORDER_PREFIX + IdUtil.getSnowflakeNextIdStr())  // 订单编号
                .userName(member.getUserName())           // 用户名
                .userAvatar(member.getAvatar())           // 头像地址
                .userScore(member.getScore().toString())  // 积分
//                .payType(ServiceConstants.PAY_TYPE_1)       // 付款方式，订单结束再付款
                .beginTime(new Date())     // 单车开始使用时间
                .createBy("admin")   // 创建人
                .createTime(new Date())   // 创建时间
                .remark("订单未结束").build();  // 备注

        // 2.新增一条订单记录
        orderMapper.insertSelective(order);

        // 3.更新会员状态 为被使用中的状态   statue=1
        orderMemberFeign.updateByPrimaryKeySelective(userId, ServiceConstants.MEMBER_STATUS_1,null,null,"正在使用单车");

        // 4.更新单车状态 为使用中的状态     statue=1
        orderBicycleFeign.updateByPrimaryKeySelective(bicycleId, ServiceConstants.BICYCLE_STATUS_1, "使用中");


    }


    /*
    *   根据 userId 分页查询会员用车记录
    *       包含会员的名字、头像和总积分信息
    * */
    @Override
    public Page<Order> getMemberOrder(Integer userId) {
        Page<Order> page = new Page<>();

        // 开启分页查询     默认 pageNum=1  pageSize=10
        com.github.pagehelper.Page<Object> pageHelper = PageHelper.startPage(page.getPageNum(), page.getPageSize());
        // 执行分页查询
        List<Order> orderList = orderMapper.getMemberOrder(userId);

        // 总条数
        page.setTotalCount(pageHelper.getTotal());
        // 总页数
        page.setTotalPage(pageHelper.getPages());
        // 分页数据
        page.setData(orderList);   // pageHelper.getResult()

        return page;
    }


    /*
     *   会员锁车还车
     *       步骤：1.根据 orderSn 查询订单数据：订单开始时间 beginTime
     *             2.计算时间间隔，单位为：分钟，并计算出要支付的金额
     *             3.根据 userId 查询会员信息 (money、score...)，并进行判断
     *             4.更新会员、更新车辆、更新订单
     * */
    @Override
    @Transactional   // 多次进行与数据库进行 修改 IO操作，所以得加事务
    public void backBicycle(Integer userId, String orderSn) {
        // 1.根据 orderSn 查询订单
        Order order = orderMapper.selectMemberByOrderSn(orderSn);
        // 获取 订单开始的时间
        Date beginTime = order.getBeginTime();

        // 2.计算时间间隔：当前时间 - 开始用车时间
        // 用车时间(单位：毫秒)
        double time = (new Date().getTime() - beginTime.getTime());
        // 用车时间(单位：分钟)
        time = time / (1000*60);
        /*
        *   不足一分钟，按一分钟计算
        *       方法：1.time % 1 ，得到整数部分，再用time - 整数部分，得到小数部分，如果小数部分大于0，整数部分加一，得到最终的用车时间
        *             2.将time转化为 String 类型，通过 split() 方法取整数和小数部分，如果小数部分大于0，整数部分加一，得到最终的用车时间
        *             3.直接 Math.ceil()
        * */
        time = Math.ceil(time);
        // 要支付的金额
        Double payMoney = time * 1.5;

        // 3.根据 userId 获取会员信息
        Member member = orderMemberFeign.selectByPrimaryKey(userId);
        // 获取余额
        BigDecimal money = member.getMoney();
        // 获取积分
        Integer score = member.getScore();


        /*
        *   判断 积分score 是奇数还是偶数
        * */
        // score为奇数：使用完积分之后，积分剩余1
        if (score%2!=0) {

            // 一个积分无法兑换金额，所以如果积分为奇数，最后会留下一个积分，如果支付成功，则会留下两个积分
            score = score - 1;

            // 计算积分兑换的金额
            double deductionMoney = (score/2) * 0.5;

            // 判断金额是否充足，并更新订单表、会员表、单车表
            judgeAndUpdate(userId, order, payMoney, money, deductionMoney,1);

            // 放开锁
            stringRedisTemplate.delete("lock:"+ order.getBicycleId());

            return;
        }
        // score为偶数：使用完积分之后，积分为0
        // 计算积分兑换的金额
        double deductionMoney = (score/2) * 0.5;
        // 判断金额是否充足，并更新订单表、会员表、单车表
        judgeAndUpdate(userId,order,payMoney,money,deductionMoney,0);

        // 放开锁
        stringRedisTemplate.delete("lock:"+ order.getBicycleId());

    }


    /*
    *   判断金额是否充足，并更新订单表、会员表、单车表
    * */
    private void judgeAndUpdate(Integer userId, Order order, Double payMoney, BigDecimal money, double deductionMoney, Integer score) {
        /*
         *   判断 兑换金额+余额 是否足够支付                         订单表其实还可以还设置一个状态字段status，来表示订单是否结束
         *       不够：抛出异常，让用户去充值
         *       足够：1.更新订单：积分(+1)、付款方式(1)、订单结束时间
         *             2.更新会员：状态(status=1 --> status=0)、充值金额、积分(+1)
         *             3.更新车辆：状态(status=1 --> status=0)
         * */
        // 如果不够支付
        if (deductionMoney + money.doubleValue() < payMoney) {

            // 抛出异常
            throw new ServiceException("抱歉，您的余额不足，请先充值");       // 怪不得要交押金

        }
        // 到这里说明足够支付
        // 计算余额
        money = money
                .add(new BigDecimal(deductionMoney))    // 加上积分抵扣金额
                .subtract(new BigDecimal(payMoney));    // 减去要支付的金额
        // 更新会员  增加一个积分
        orderMemberFeign.updateByPrimaryKeySelective(userId, ServiceConstants.MEMBER_STATUS_0, money, score++,"未用车");
        // 更新车辆
        orderBicycleFeign.updateByPrimaryKeySelective(order.getBicycleId(),ServiceConstants.BICYCLE_STATUS_0,"未使用");
        // 更新订单
        updateOrder(order, score, "交易成功");
    }


    /*
    *   更新订单
    * */
    private void updateOrder(Order order, Integer score, String remark) {
        order.setPayType(ServiceConstants.PAY_TYPE_1);  // 付款方式   1：支付宝
        order.setUserScore(score.toString());           // 积分
        order.setEndTime(new Date());                   // 订单结束时间
        order.setUpdateBy("admin");
        order.setUpdateTime(new Date());
        order.setRemark(remark);                        // 备注
        orderMapper.updateByPrimaryKeySelective(order);
    }



}
